// 实现原型链的基本模式
function SuperType () {
  this.property = true
}
SuperType.prototype.getSuperValue = function () {
  return this.property
}
function SubType () {
  this.sunProperty = false
}
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function () {
  return this.sunProperty
}
var instance = new SubType()
console.log(instance.getSuperValue()) // true

// 这种重写原型的方式，会使得 instance 的 constructor 属性指向 SuperType,因为最初的原型里的 constructor 是指向 Subtype 的，但是我们重写覆盖丢掉了默认的。新原型是 superType 的实例
console.log('原型被重写了', instance.constructor) //  ƒ SuperType ()

// 重写超类型中的方法 覆盖
SubType.prototype.getSuperValue = function () {
  return false
}
console.log(instance.getSuperValue()) // false
// 注意  原型的动态性并没有违背。因为你访问方法或对象的时候，是在修改原型之前一个，之后一个。所以修改前后的结果不一样。

var friend = new Object()
// console.log('调用方法在修改原型之前', friend.say()) // 报错

Object.prototype.say = function (){
  return '111'
}
console.log('调用方法在修改原型之后', friend.say()) // 111

// 不能使用对象字面量的方式创建原型方法
function SuperType () {
  this.property = true
}
SuperType.prototype.getSuperValue = function () {
  return this.property
}
function SubType () {
  this.subProperty = false
}
SubType.prototype = new SuperType()
SubType.prototype = { // 重写了原型,相当于你上面的赋值操作都被覆盖了
  getSubValue: function () {
    return this.subProperty
  },
  someOtherMethod: function () {
    return false
  }
}
var instance = new SubType()
// try {
//   console.log(instance.getSuperValue()) // 报错,try-catch也没用
// } catch (e) {
//   console.log(e)
// }

// 原型链存在的问题
function SuperType1 () {
  this.colors = ['red', 'blue', 'green']
}
function SubType1 () {

}
SubType1.prototype = new SuperType1()
var instance1 = new SubType1()
instance1.colors.push('black')
console.log(instance1.colors) // ["red", "blue", "green", "black"]

var instance2 = new SubType1()
console.log(instance2.colors)// ["red", "blue", "green", "black"]

// 借用构造函数
function SuperType () {
  this.colors = ['red', 'blue', 'green']
}
function SubType () {
  SuperType.call(this)
}
var instance1 = new SubType()
instance1.colors.push('black')
console.log(instance1.colors) // ["red", "blue", "green", "black"]

var instance2 = new SubType()
console.log(instance2.colors) // ["red", "blue", "green"]

// 关于这种继承方法的无法继承原型
function SuperType () { //构造函数中的this用new 调用时,指向this
  this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.getColors = function () {
  console.log('aa')
}
function SubType () {
  SuperType.call(this)
}
var instance = new SubType()
if (instance.getColors) {
  instance.getColors()
} else {
  console.log('无法拿到超类型原型上的方法') // 无法拿到超类型原型上的方法
}


// 组合继承  这种方式的弊端就是实例属性被放入新对象,放入了两次,只不过两次都是一样的,后者覆盖了前者
function SuperType (name) {
  this.name = name
  this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
  console.log(this.name)
}

function SubType (name, age) {
  SuperType.call(this, name)
  this.age = age
}
// 原型继承 new SuperType() = {...实例属性, Object __proto__}
SubType.prototype = new SuperType()
// 更改子类constructor的指向
SubType.prototype.constructor = SubType
// 给子类原型添加了一个属性,相当于给父类的实例添加了一个属性
SubType.prototype.sayAge = function () {
  console.log(this.age)
}
// new SubType的过程  1.{} 2.age -> {} 3.执行父类的代码,相当于将父类的实例属性,作为了自己的实例属性 4.return obj
var instance1 = new SubType('Nicholas', 29)
// 实例中的colors,push了一个black
instance1.colors.push('black') //
console.log(instance1.colors) // ["red", "blue", "green", "black"]
instance1.sayName() // Nicholas
instance1.sayAge() // 29

var instance2 = new SubType('Greg', 27)
console.log(instance2.colors) // ['red', 'blue', 'green']


// 原型式继承 函数参数是按值传递的，指针其实也是一个值
// 保存的是内存地址
function object (o) {
  function F() {}
  F.prototype = o
  return new F()
}
var person = {
  name: 'Nicholas',
  friends: ['shelby', 'court', 'van']
}

var anotherPerson = object(person)
// 给新对象附加了一个实例属性，这个实例属性
// 覆盖了 person 原型中的 name，所以你访问到的就是 lee

anotherPerson.name = 'lee'

// 你要是这样去写，是设置的原型，由于原型是共享的，会影响到
// 其他额实例
// anotherPerson.__proto__.name = 'lee'

var yetPerson = object(person)
// name没有被影响
console.log(anotherPerson.name) // lee
// 这里在你读取 name 的时候，实例中是没有的
console.log(yetPerson.name) // Nicholas
// 这种的也是操作了原型，会对其他实例产生影响
anotherPerson.friends.push('monkey')
console.log(yetPerson.friends) // ["shelby", "court", "van", "monkey"]


// 因为这里时一个对象
var a = {name: 'a'}
var b = a
b.name = 'b'
console.log(a.name) // b

// Object.creat（）第一个参数
var person = {
  name: 'Nicholas',
  friends: ['shelby', 'Court', 'Van']
}
var anotherPerson = Object.create(person)
// 注意你这里并不是修改，而是创建了一个与原型同名的实例属性
anotherPerson.name = 'Greg'
anotherPerson.friends.push('Rob')

var yetAnnotherPerson = Object.create(person)
yetAnnotherPerson.name = 'Linda'
yetAnnotherPerson.friends.push('Barbie')

console.log(person.friends) // ["shelby", "Court", "Van", "Rob", "Barbie"]


// Object.creat()第二个参数
var person = {
  name: 'Nicholas',
  friends: ['Shelby', 'Court', 'Van']
}
var anotherPerson = Object.create(person, {
  name: {
    value: '123'
  }
})
console.log(anotherPerson) // {name: "123"}

// 寄生式继承 缺点是像构造函数那样,实现不了函数复用
function object (o) {
  function F() {}
  F.prototype = o
  return new F()
}
function creatAnother (original) {
  var clone = object(original)
  clone.sayHi = function () {
    console.log('Hi')
  }
  return clone
}
var person = {
  name: 'Nicholas',
  friends: ['Shelby', 'Court', 'Van']
}
var anotherPerson = creatAnother(person)

// 寄生组合式继承 开发人员认为的最理想的继承范式 这种方式能够剥离子类对象上, 父类实例的实例属性
function onheritPrototype (subType, superType) {
  var prototype = object(superType.prototype)
  prototype.constructor = subType
  subType.prototype = prototype
}
function SuperType (name) {
  this.name = name
  this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
  console.log(this.name)
}
function SubType (name, age) {
  SuperType.call(this, name)
  this.age = age
}
onheritPrototype(subType, superType)
SubType.property.sayAge = function () {
  console.log(this.age)
}